/*
 * HeapSource.c
 *
 *  Created on: 2011-7-7
 *      Author: Donghan
 */
#include "helper/mspace.h"
#include <limits.h>     // for INT_MAX
#include <sys/mman.h>
#include <errno.h>

#include "../Dalvik.h"
#include "Heap.h"
#include "HeapInternal.h"
#include "HeapSource.h"
#include "HeapBitmap.h"

// TODO: find a real header file for these.
extern int dlmalloc_trim(size_t);
extern void dlmalloc_walk_free_pages(void(*)(void*, void*, void*), void*);

static void snapIdealFootprint(void);
static void setIdealFootprint(size_t max);

#define ALIGN_UP_TO_PAGE_SIZE(p) \
    (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
#define ALIGN_DOWN_TO_PAGE_SIZE(p) \
    ((size_t)(p) & ~(SYSTEM_PAGE_SIZE - 1))

#define HEAP_UTILIZATION_MAX        1024
#define DEFAULT_HEAP_UTILIZATION    512     // Range 1..HEAP_UTILIZATION_MAX
#define HEAP_IDEAL_FREE             (2 * 1024 * 1024)
#define HEAP_MIN_FREE               (HEAP_IDEAL_FREE / 4)

/* Start a concurrent collection when free memory falls under this
 * many bytes.
 */
#define CONCURRENT_START (128 << 10)

/* The next GC will not be concurrent when free memory after a GC is
 * under this many bytes.
 */
#define CONCURRENT_MIN_FREE (CONCURRENT_START + (128 << 10))

#define HS_BOILERPLATE() \
    do { \
        assert(gDvm.gcHeap != NULL); \
        assert(gDvm.gcHeap->heapSource != NULL); \
        assert(gHs == gDvm.gcHeap->heapSource); \
    } while (0)

#define DEBUG_HEAP_SOURCE 0
#if DEBUG_HEAP_SOURCE
#define HSTRACE(...)  LOG(LOG_INFO, LOG_TAG "-hs", __VA_ARGS__)
#else
#define HSTRACE(...)  /**/
#endif

/*
 * How will this be used?
 * allocating/freeing: Heap.c just wants to say "alloc(n)" and get a ptr
 * 		- if allocating in large doesn't work, try allocating from small
 * Heap.c will use HeapSource.h; HeapSource.c will do the right thing
 * 		between small and large
 * 		- some operations should be abstracted; put in a structure
 *
 * How do we manage the size trade-offs?
 * ...
 */
typedef struct {
    /* The mspace to allocate from.
     */
    mspace msp;

    /* The largest size that this heap is allowed to grow to.
     */
    size_t absoluteMaxSize;

    /* Number of bytes allocated from this mspace for objects,
     * including any overhead.  This value is NOT exact, and
     * should only be used as an input for certain heuristics.
     */
    size_t bytesAllocated;

    /* Number of bytes allocated from this mspace at which a
     * concurrent garbage collection will be started.
     */
    size_t concurrentStartBytes;

    /* Number of objects currently allocated from this mspace.
     */
    size_t objectsAllocated;

    /*
     * The lowest address of this heap, inclusive.
     */
    char *base;

    /*
     * The highest address of this heap, exclusive.
     */
    char *limit;
} Heap;

struct HeapSource {
	/* Target ideal heap utilization ratio; range 1..HEAP_UTILIZATION_MAX
	     */
	    size_t targetUtilization;

	    /* Requested minimum heap size, or zero if there is no minimum.
	     */
	    size_t minimumSize;

	    /* The starting heap size.
	     */
	    size_t startSize;

	    /* The largest that the heap source as a whole is allowed to grow.
	     * 堆允许增长的最大空间
	     */
	    size_t absoluteMaxSize;

	    /* The desired max size of the heap source as a whole.
	     */
	    size_t idealSize;

	    /* The maximum number of bytes allowed to be allocated from the
	     * active heap before a GC is forced.  This is used to "shrink" the
	     * heap in lieu of actual compaction.
	     */
	    size_t softLimit;

	    /* The heaps; heaps[0] is always the active heap,
	     * which new objects should be allocated from.
	     */
	    Heap heaps[HEAP_SOURCE_MAX_HEAP_COUNT];

	    /* The current number of heaps.
	     */
	    size_t numHeaps;

	    /* External allocation count.
	     */
	    size_t externalBytesAllocated;

	    /* The maximum number of external bytes that may be allocated.
	     */
	    size_t externalLimit;

	    /* True if zygote mode was active when the HeapSource was created.
	     * zygote活动就为true
	     */
	    bool sawZygote;
	    /*
	     * The base address of the virtual memory reservation.
	     */
	    char *heapBase;

	    /*
	     * The length in bytes of the virtual memory reservation.
	     */
	    size_t heapLength;

	    /*
	     * The live object bitmap.
	     */
	    HeapBitmap liveBits;

	    /*
	     * The mark bitmap.
	     */
	    HeapBitmap markBits;

	    /*
	     * State for the GC daemon.
	     */
	    bool hasGcThread;
	    pthread_t gcThread;
	    bool gcThreadShutdown;
	    pthread_mutex_t gcThreadMutex;
	    pthread_cond_t gcThreadCond;
};

#define hs2heap(hs_) (&((hs_)->heaps[0]))

/*
 * Returns true iff a soft limit is in effect for the active heap.
 */
static inline bool softLimited(const HeapSource *hs)
{
    /* softLimit will be either INT_MAX(2G) or the limit for the
     * active mspace.  idealSize can be greater than softLimit
     * if there is more than one heap.  If there is only one
     * heap, a non-INT_MAX softLimit should always be the same
     * as idealSize.
     * 只有一个堆时softLimit应该等于idealSize
     */
    return hs->softLimit <= hs->idealSize;
}

/*
 * Returns approximately the maximum number of bytes allowed to be
 * allocated from the active heap before a GC is forced.
 */
static size_t getAllocLimit(const HeapSource *hs)
{
    if (softLimited(hs)) {
        return hs->softLimit;
    } else {
        return mspace_max_allowed_footprint(hs2heap(hs)->msp);
    }
}

/*
 * Returns the current footprint of all heaps.  If includeActive
 * is false, don't count the heap at index 0.
 * 返回当前heap堆的大小，如果includeActive为false，不计算0号位的。。。从1开始
 */
static inline size_t oldHeapOverhead(const HeapSource *hs, bool includeActive)
{
	size_t footprint = 0;
	size_t i;

	if(includeActive){
		i = 0;
	}else{
		i = 1;
	}
	for(;i<hs->numHeaps;i++){
		footprint += mspace_footprint(hs->heaps[i].msp);
	}
	return footprint;
}

/*
 * Returns the heap that <ptr> could have come from, or NULL
 * if it could not have come from any heap.
 */
static inline Heap *ptr2heap(const HeapSource *hs, const void *ptr)
{
    const size_t numHeaps = hs->numHeaps;
    size_t i;

//TODO: unroll this to HEAP_SOURCE_MAX_HEAP_COUNT
    if (ptr != NULL) {
        for (i = 0; i < numHeaps; i++) {
            const Heap *const heap = &hs->heaps[i];

            if ((const char *)ptr >= heap->base && (const char *)ptr < heap->limit) {
                return (Heap *)heap;
            }
        }
    }
    return NULL;
}

/*
 * Functions to update heapSource->bytesAllocated when an object
 * is allocated or freed.  mspace_usable_size() will give
 * us a much more accurate picture of heap utilization than
 * the requested byte sizes would.
 *
 * These aren't exact, and should not be treated as such.
 * 每当对象分配或释放时，更新heapsource中已经分配的字节数（不准确）
 * mspace_usable_size()提供更准确的数据
 */
static inline void countAllocation(Heap *heap, const void *ptr, bool isObj)
{
	HeapSource *hs;
	assert(heap->bytesAllocated < mspace_footprint(heap->msp));

	heap->bytesAllocated += mspace_usable_size(heap->msp, ptr) + HEAP_SOURCE_CHUNK_OVERHEAD;
	if(isObj){
		heap->objectsAllocated++;
		hs = gDvm.gcHeap->heapSource;
		dvmHeapBitmapSetObjectBit(&hs->liveBits, ptr);
	}

	assert(heap->bytesAllocated < mspace_footprint(heap->msp));
}

static void countFree(Heap *heap, const void *ptr, size_t *numBytes)
{

}

static HeapSource *gHs = NULL;

static mspace createMspace(void *base, size_t startSize, size_t absoluteMaxSize)
{
	mspace msp;

	/* Create an unlocked dlmalloc mspace to use as
	 * a small-object heap source.
	 *
	 * We start off reserving heapSizeStart/2 bytes but
	 * letting the heap grow to heapSizeStart.  This saves
	 * memory in the case where a process uses even less
	 * than the starting size.
	 * 开始的时候我们只使用二分之一的内存空间，但是会让内存增长。
	 */
	errno = 0;
	msp = create_contiguous_mspace_with_base(startSize/2,absoluteMaxSize, false, base);
	if (msp != NULL) {
	    /* Don't let the heap grow past the starting size without
	     * our intervention.
	     */
	    mspace_set_max_allowed_footprint(msp, startSize);
	} else {
	    /* There's no guarantee that errno has meaning when the call
	     * fails, but it often does.
	     */
	}

	return msp;
}

static bool addNewHeap(HeapSource *hs, mspace *msp, size_t mspAbsoluteMaxSize)
{
	Heap heap;

	if (hs->numHeaps >= HEAP_SOURCE_MAX_HEAP_COUNT) {
	    dvmAbort();
	    return false;
	}
	memset(&heap, 0, sizeof(heap));
	if (msp != NULL) {
	    heap.msp = msp;
	    heap.absoluteMaxSize = mspAbsoluteMaxSize;
	    heap.concurrentStartBytes = SIZE_MAX;
	    heap.base = hs->heapBase;
	    heap.limit = hs->heapBase + heap.absoluteMaxSize;
	}
	else
	{
		void *sbrk0 = contiguous_mspace_sbrk0(hs->heaps[0].msp);
		char *base = (char *)ALIGN_UP_TO_PAGE_SIZE(sbrk0);
		size_t overhead = base - hs->heaps[0].base;

		assert(((size_t)hs->heaps[0].base & (SYSTEM_PAGE_SIZE - 1)) == 0);
		if (overhead + HEAP_MIN_FREE >= hs->absoluteMaxSize) {
			return false;
		}
		hs->heaps[0].absoluteMaxSize = overhead;
		hs->heaps[0].limit = base;
		heap.absoluteMaxSize = hs->absoluteMaxSize - overhead;
		heap.msp = createMspace(base, HEAP_MIN_FREE, heap.absoluteMaxSize);
		heap.concurrentStartBytes = HEAP_MIN_FREE - CONCURRENT_START;
		heap.base = base;
		heap.limit = heap.base + heap.absoluteMaxSize;
		if (heap.msp == NULL) {
		    return false;
		}
	}
	 /* Don't let the soon-to-be-old heap grow any further.
	  */
	 if (hs->numHeaps > 0) {
	     mspace msp = hs->heaps[0].msp;
	     mspace_set_max_allowed_footprint(msp, mspace_footprint(msp));
	 }

	  /* Put the new heap in the list, at heaps[0].
	   * Shift existing heaps down.
	   */
	 memmove(&hs->heaps[1], &hs->heaps[0], hs->numHeaps * sizeof(hs->heaps[0]));
	 hs->heaps[0] = heap;
	 hs->numHeaps++;

	 return true;
}

/*
 * The garbage collection daemon.  Initiates a concurrent collection
 * when signaled.
 */
static void *gcDaemonThread(void* arg)
{
}

static bool gcDaemonStartup(void)
{
    dvmInitMutex(&gHs->gcThreadMutex);
    pthread_cond_init(&gHs->gcThreadCond, NULL);
    gHs->gcThreadShutdown = false;
    gHs->hasGcThread = dvmCreateInternalThread(&gHs->gcThread, "GC",gcDaemonThread, NULL);
    return gHs->hasGcThread;
}

static void gcDaemonShutdown(void)
{
	if (gHs->hasGcThread) {
        dvmLockMutex(&gHs->gcThreadMutex);
        gHs->gcThreadShutdown = true;
        dvmSignalCond(&gHs->gcThreadCond);
        dvmUnlockMutex(&gHs->gcThreadMutex);
        pthread_join(gHs->gcThread, NULL);
    }
}

/*
 * Initializes the heap source; must be called before any other
 * dvmHeapSource*() functions.  Returns a GcHeap structure
 * allocated from the heap source.
 * 对内存的大小的真正分配
 * 必须在调用所有堆分配函数前使用
 */
GcHeap* dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize)
{
	GcHeap *gcHeap;
	HeapSource *hs;
	mspace msp;
	size_t length;
	void *base;

	assert(gHs == NULL);

	//大于最大分配堆大小的时候报错，返回空值
	if(startSize > absoluteMaxSize){
		return NULL;
	}

	/*
	 * Allocate a contiguous region of virtual memory to subdivided
	 * among the heaps managed by the garbage collector.
	 * 申请一块由gc管理的虚拟内存
	 */
	length = ALIGN_UP_TO_PAGE_SIZE(absoluteMaxSize);
/*
 * Allocates a memory region using ashmem and mmap, initialized to
 *  zero.  Actual allocation rounded up to page multiple.  Returns
 *  NULL on failure.
 *  使用ashmem和mmap申请一段内存空间，初始化为0。实际的分配会形成很多的页，失败
 *  返回空值。
 */
	base = dvmAllocRegion(length, PROT_NONE, "dalvik-heap");
	if (base == NULL) {
	    return NULL;
	}

	/*
	 * Create an unlocked dlmalloc mspace to use as
	 * the small object heap source.
	 * 创建一个小的不上锁的指针来存放小的对象资源
	 */
	msp = createMspace(startSize, absoluteMaxSize, 0);
	if(msp == NULL){
		goto fail;
	}
	/* Allocate a descriptor from the heap we just created.
	 * 在内存(空间)上创建一个GC堆(包括GC堆的管理数据)
	 *
	 * 给定特定的指针和大小进行创建
	 */
	gcHeap = mspace_malloc(msp,sizeof(*gcHeap));
	if(gcHeap == NULL){
		goto fail;
	}
	memset(gcHeap,0,sizeof(*gcHeap));

	hs = mspace_malloc(msp,sizeof(*hs));
	if(hs == NULL){
		goto fail;
	}
	memset(hs,0,sizeof(*hs));

	hs->targetUtilization = DEFAULT_HEAP_UTILIZATION;
	/* Requested minimum heap size, or zero if there is no minimum.
	 * //分配的堆的最小值
	 */
	hs->minimumSize = 0;
	/* The starting heap size.
	 * //堆分配的初始大小
	 */
	hs->startSize = startSize;
	/* The largest that the heap source as a whole is allowed to grow.
	 * //允许分配的堆增长到的最大尺寸
	 */
	hs->absoluteMaxSize = absoluteMaxSize;
	/* The desired max size of the heap source as a whole.
	 * //理想的堆的最大大小
	 */
	hs->idealSize = startSize;
	/* The maximum number of bytes allowed to be allocated from the
	 * active heap before a GC is forced.  This is used to "shrink" the
	 * heap in lieu of actual compaction.
	 * //在垃圾收集前允许堆被分配的最大大小
	 */
	hs->softLimit = INT_MAX;    // no soft limit at first
	/* The current number of heaps.
	 * //当前的堆的个数
	 */
	hs->numHeaps = 0;
	 /* True if zygote mode was active when the HeapSource was created.
	  * 在创建这个HeapSource的时候是否是zygote模式也就是是否有zygote进程
	  */
	hs->sawZygote = gDvm.zygote;
	hs->hasGcThread = false;
	hs->heapBase = base;
	hs->heapLength = length;
	if (!addNewHeap(hs, msp, absoluteMaxSize)) {
	      goto fail;
	}
	if (!dvmHeapBitmapInit(&hs->liveBits, base, length, "dalvik-bitmap-1")) {
	    goto fail;
	}
	if (!dvmHeapBitmapInit(&hs->markBits, base, length, "dalvik-bitmap-2")) {
	    dvmHeapBitmapDelete(&hs->liveBits);
	    goto fail;
	}

	gcHeap->markContext.bitmap = &hs->markBits;
	//堆源数据结构，包含了所有的堆和和堆有关的信息
	gcHeap->heapSource = hs;

	countAllocation(hs2heap(hs), gcHeap, false);
	countAllocation(hs2heap(hs), hs, false);

	//设置全局变量
	gHs = hs;
	return gcHeap;

fail:
	munmap(base, length);
	return NULL;
}

/*
 * If the HeapSource was created while in zygote mode, this
 * will create a new heap for post-zygote allocations.
 * Having a separate heap should maximize the number of pages
 * that a given app_process shares with the zygote process.
 *
 * Android 作业系统开机时，会经由 init.rc 来启动许多外部程序，其中有一个最重要
 * process 称为 Zygote。Zygote 是 Android 的 monitor process，它主要负责
 * 两项工作： 1. 启动 system server 2. 执行 Android 应用程序
 *「System Server」是由 Zygote 所建立的另外一个 process，建立 system server
 * 的方式是使用典型的 Linux system call - fork()。当 Zygote 成功建立 system server
 * 后，便进入 socket listening 模式。在此模式下，zygote 会监听（listen）由 socket 所传入的「命令」
 * 并依据命令的內容启动 Android 应用程序。 Zygote 启动外部 Android 用用程序的方式，同样是使用
 * Linux kernel 所提供的 fork() system call。因此，在 socket 做 listening，并依据命令來
 * fork() 并执行外部 Android 应用程序，称之为「Zygote Mode」。
 *
 * 有新的heap产生，进行分配空间
 */
bool dvmHeapSourceStartupAfterZygote()
{
	return gDvm.concurrentMarkSweep ? gcDaemonStartup() : true;
}

/*
 * This is called while in zygote mode, right before we fork() for the
 * first time.  We create a heap for all future zygote process allocations,
 * in an attempt to avoid touching pages in the zygote heap.  (This would
 * probably be unnecessary if we had a compacting GC -- the source of our
 * troubles is small allocations filling in the gaps from larger ones.)
 */
bool dvmHeapSourceStartupBeforeFork()
{
    HeapSource *hs = gHs; // use a local to avoid the implicit "volatile"

    HS_BOILERPLATE();

    assert(gDvm.zygote);

    if (!gDvm.newZygoteHeapAllocated) {
        /* Create a new heap for post-fork zygote allocations.  We only
         * try once, even if it fails.
         */
        gDvm.newZygoteHeapAllocated = true;
        return addNewHeap(hs, NULL, 0);
    }
    return true;
}

void dvmHeapSourceThreadShutdown(void)
{
    if (gDvm.gcHeap != NULL && gDvm.concurrentMarkSweep) {
        gcDaemonShutdown();
    }
}

/*
 * Tears down the entire GcHeap structure and all of the substructures
 * attached to it.  This call has the side effect of setting the given
 * gcHeap pointer and gHs to NULL.
 */
void dvmHeapSourceShutdown(GcHeap **gcHeap)
{
	if (*gcHeap != NULL && (*gcHeap)->heapSource != NULL) {
	    HeapSource *hs;

	    hs = (*gcHeap)->heapSource;

	    assert((char *)*gcHeap >= hs->heapBase);
	    assert((char *)*gcHeap < hs->heapBase + hs->heapLength);

	    dvmHeapBitmapDelete(&hs->liveBits);
	    dvmHeapBitmapDelete(&hs->markBits);

	    munmap(hs->heapBase, hs->heapLength);
	    gHs = NULL;
	    *gcHeap = NULL;
	}
}

/*
 * Gets the begining of the allocation for the HeapSource.
 */
void *dvmHeapSourceGetBase(void)
{
    return gHs->heapBase;
}

/*
 * Returns the requested value. If the per-heap stats are requested, fill
 * them as well.
 *
 * Caller must hold the heap lock.
 */
size_t
dvmHeapSourceGetValue(enum HeapSourceValueSpec spec, size_t perHeapStats[],
                      size_t arrayLen)
{
}

static void aliasBitmap(HeapBitmap *dst, HeapBitmap *src, uintptr_t base, uintptr_t max)
{
}


/*
 * Initializes a vector of object and mark bits to the object and mark
 * bits of each heap.  The bits are aliased to the heapsource
 * object and mark bitmaps.  This routine is used by the sweep code
 * which needs to free each object in the correct heap.
 */
void dvmHeapSourceGetObjectBitmaps(HeapBitmap liveBits[], HeapBitmap markBits[],
                                   size_t numHeaps)
{
}

/*
 * Get the bitmap representing all live objects.
 */
HeapBitmap *dvmHeapSourceGetLiveBits(void)
{
    HS_BOILERPLATE();

    return &gHs->liveBits;
}

void dvmHeapSourceSwapBitmaps(void)
{
    HeapBitmap tmp;

    tmp = gHs->liveBits;
    gHs->liveBits = gHs->markBits;
    gHs->markBits = tmp;
}

void dvmHeapSourceZeroMarkBitmap(void)
{

}

void dvmMarkImmuneObjects(const char *immuneLimit)
{
}

/*
 * Allocates <n> bytes of zeroed data.
 */
void *
dvmHeapSourceAlloc(size_t n)
{
}


/* Remove any hard limits, try to allocate, and shrink back down.
 * Last resort when trying to allocate an object.
 */
static void *heapAllocAndGrow(HeapSource *hs, Heap *heap, size_t n)
{
}

/*
 * Allocates <n> bytes of zeroed data, growing as much as possible
 * if necessary.
 * 分配n bytes空间，并增长到足够大
 */
void *dvmHeapSourceAllocAndGrow(size_t n)
{

}

/*
 * Frees the first numPtrs objects in the ptrs list and returns the
 * amount of reclaimed storage. The list must contain addresses all in
 * the same mspace, and must be in increasing order. This implies that
 * there are no duplicates, and no entries are NULL.
 */
size_t dvmHeapSourceFreeList(size_t numPtrs, void **ptrs)
{

}

/*
 * Returns true iff <ptr> is in the heap source.
 */
bool
dvmHeapSourceContainsAddress(const void *ptr)
{
    HS_BOILERPLATE();

    return (dvmHeapBitmapCoversAddress(&gHs->liveBits, ptr));
}

/*
 * Returns true iff <ptr> was allocated from the heap source.
 */
bool dvmHeapSourceContains(const void *ptr)
{
    HS_BOILERPLATE();

    if (dvmHeapSourceContainsAddress(ptr)) {
        return dvmHeapBitmapIsObjectBitSet(&gHs->liveBits, ptr) != 0;
    }
    return false;
}

/*
 * Returns the value of the requested flag.
 */
bool dvmHeapSourceGetPtrFlag(const void *ptr, enum HeapSourcePtrFlag flag)
{
}

/*
 * Returns the number of usable bytes in an allocated chunk; the size
 * may be larger than the size passed to dvmHeapSourceAlloc().
 */
size_t dvmHeapSourceChunkSize(const void *ptr)
{
    Heap *heap;

    HS_BOILERPLATE();

    heap = ptr2heap(gHs, ptr);
    if (heap != NULL) {
        return mspace_usable_size(heap->msp, ptr);
    }
    return 0;
}

/*
 * Returns the number of bytes that the heap source has allocated
 * from the system using sbrk/mmap, etc.
 *
 * Caller must hold the heap lock.
 */
size_t dvmHeapSourceFootprint()
{
    HS_BOILERPLATE();

//TODO: include size of bitmaps?
    return oldHeapOverhead(gHs, true);
}

/*
 * Return the real bytes used by old heaps and external memory
 * plus the soft usage of the current heap.  When a soft limit
 * is in effect, this is effectively what it's compared against
 * (though, in practice, it only looks at the current heap).
 */
static size_t getSoftFootprint(bool includeActive)
{
    HeapSource *hs = gHs;
    size_t ret;

    HS_BOILERPLATE();

    ret = oldHeapOverhead(hs, false) + hs->externalBytesAllocated;
    if (includeActive) {
        ret += hs->heaps[0].bytesAllocated;
    }

    return ret;
}

/*
 * Gets the maximum number of bytes that the heap source is allowed
 * to allocate from the system.
 */
size_t
dvmHeapSourceGetIdealFootprint()
{
    HeapSource *hs = gHs;

    HS_BOILERPLATE();

    return hs->idealSize;
}

/*
 * Sets the soft limit, handling any necessary changes to the allowed
 * footprint of the active heap.
 */
static void setSoftLimit(HeapSource *hs, size_t softLimit)
{

}

/*
 * Sets the maximum number of bytes that the heap source is allowed
 * to allocate from the system.  Clamps to the appropriate maximum
 * value.
 */
static void setIdealFootprint(size_t max)
{

}

/*
 * Make the ideal footprint equal to the current footprint.
 */
static void
snapIdealFootprint()
{
}

/*
 * Gets the current ideal heap utilization, represented as a number
 * between zero and one.
 */
float dvmGetTargetHeapUtilization()
{
}

/*
 * Sets the new ideal heap utilization, represented as a number
 * between zero and one.
 */
void dvmSetTargetHeapUtilization(float newTarget)
{
}

/*
 * If set is true, sets the new minimum heap size to size; always
 * returns the current (or previous) size.  If size is negative,
 * removes the current minimum constraint (if present).
 */
size_t dvmMinimumHeapSize(size_t size, bool set)
{
}

/*
 * Given the size of a live set, returns the ideal heap size given
 * the current target utilization and MIN/MAX values.
 *
 * targetUtilization is in the range 1..HEAP_UTILIZATION_MAX.
 */
static size_t getUtilizationTarget(size_t liveSize, size_t targetUtilization)
{
}

/*
 * Given the current contents of the active heap, increase the allowed
 * heap footprint to match the target utilization ratio.  This
 * should only be called immediately after a full mark/sweep.
 */
void dvmHeapSourceGrowForUtilization()
{

}

/*
 * Return free pages to the system.
 * TODO: move this somewhere else, especially the native heap part.
 */

static void releasePagesInRange(void *start, void *end, void *nbytes)
{

}

/*
 * Return unused memory to the system if possible.
 */
void
dvmHeapSourceTrim(size_t bytesTrimmed[], size_t arrayLen)
{



}

/*
 * Walks over the heap source and passes every allocated and
 * free chunk to the callback.
 */
void
dvmHeapSourceWalk(void(*callback)(const void *chunkptr, size_t chunklen,
                                      const void *userptr, size_t userlen,
                                      void *arg),
                  void *arg)
{
}

/*
 * Gets the number of heaps available in the heap source.
 *
 * Caller must hold the heap lock, because gHs caches a field
 * in gDvm.gcHeap.
 */
size_t
dvmHeapSourceGetNumHeaps()
{
    HeapSource *hs = gHs;

    HS_BOILERPLATE();

    return hs->numHeaps;
}


/*
 * External allocation tracking
 *
 * In some situations, memory outside of the heap is tied to the
 * lifetime of objects in the heap.  Since that memory is kept alive
 * by heap objects, it should provide memory pressure that can influence
 * GCs.
 */

/*
 * Returns true if the requested number of bytes can be allocated from
 * available storage.
 */
static bool externalBytesAvailable(const HeapSource *hs, size_t numBytes)
{

}

#define EXTERNAL_TARGET_UTILIZATION 820  // 80%

/*
 * Tries to update the internal count of externally-allocated memory.
 * If there's enough room for that memory, returns true.  If not, returns
 * false and does not update the count.
 *
 * The caller must ensure externalBytesAvailable(hs, n) == true.
 */
static bool externalAlloc(HeapSource *hs, size_t n, bool grow)
{

}

static void gcForExternalAlloc(bool collectSoftReferences)
{

}

/*
 * Returns true if there is enough unused storage to perform an
 * external allocation of the specified size.  If there insufficient
 * free storage we try to releasing memory from external allocations
 * and trimming the heap.
 */
static bool externalAllocPossible(const HeapSource *hs, size_t n)
{

}

/*
 * Updates the internal count of externally-allocated memory.  If there's
 * enough room for that memory, returns true.  If not, returns false and
 * does not update the count.
 *
 * May cause a GC as a side-effect.
 */
bool dvmTrackExternalAllocation(size_t n)
{

}

/*
 * Reduces the internal count of externally-allocated memory.
 */
void dvmTrackExternalFree(size_t n)
{

}

/*
 * Returns the number of externally-allocated bytes being tracked by
 * dvmTrackExternalAllocation/Free().
 */
size_t dvmGetExternalBytesAllocated()
{

}

void *dvmHeapSourceGetImmuneLimit(GcMode mode)
{

}



